#include "user_threads.h"
#include "user_mutex.h"
#include "user_io.h"
#include <stdlib.h>
#include <assert.h>

uthread_mutex_t print_mutex;

static void print_to_screen(char* str)
{
	int count = 0,writed = 0;
	int len = strlen(str);
	while (count != len) {
		writed = uthread_write(1,str+count,sizeof(char)*(len-count));
		if (writed	>=	0)
			count += writed;
	}
	return;
}

static char* itoa(int num)
{
	if	(num == 0)
		return "0";
	int num_digits=0,i=1;
	while (i <= num) {
		num_digits++;
		i *= 10;
	}
	char* str = (char*)malloc((num_digits+1)*sizeof(char));
	char tmp;
	str[num_digits] = '\0';
	for (i = (num_digits-1); i >= 0; i--){
		tmp = (char)(num % 10) + '0';
		num /= 10;
		str[i] = tmp;
	}
	return str;
}

static void block(uthread_mutex_t mutex)
{
	if (uthread_mutex_lock(mutex) != MUTEX_SUCCESS) {
		print_to_screen("Unable to lock mutex.\n");
		exit(-1);
	}
}

static void unblock(uthread_mutex_t mutex)
{
	if (uthread_mutex_unlock(mutex) != MUTEX_SUCCESS) {
		print_to_screen("Unable to unlock mutex.\n");
		exit(-1);
	}
}

static inline void delay(int n, char c) {
	int i;
	for (i = 0 ; i < n ; i++)
		i=i;
}

static void msg_with_expected_order(int i, char * in, int id){
		block(print_mutex);
		print_to_screen(itoa(i));
	  print_to_screen(": ");
		print_to_screen("Thread Id= ");
		print_to_screen(itoa(id));
		print_to_screen("   ");
		print_to_screen(in);
		print_to_screen("\n");
		unblock(print_mutex);
}

static void msg(char * in){
	msg_with_expected_order(1,in,uthread_self());
}

int f3(void* arg) {
	msg("T3 start and will yield now\n");
	msg("lll\n");
	uthread_yield();
	msg("rrrr\n");
	int i;
	assert(uthread_cancel(2)==UTHREAD_SUCCESS);
	assert(uthread_cancel(2)==UTHREAD_INVALID);//changed from fail to invalid
	msg("T2 cancelled from T3 and now T3 will join T1\n");
	assert(uthread_join(1,(int*)arg)==UTHREAD_SUCCESS);
	assert(*((int*)arg)==UTHREAD_CANCELLED);//should be cancelled(0 cancelled 1)
	for(i=0; i<100000000; i++);//
	msg("T3 end\n");
	uthread_exit(uthread_self());
	return 3;
}

int f2(void* arg) {
	msg("T2 start and will yield now\n");
	uthread_create(f3,arg);
	uthread_yield();
	msg("T2 will join T3 now\n");
	uthread_join(3, NULL);//
	msg("This is not supposed to be here!!!\n");
	return 2;
}

int f1(void* arg) {
	msg("T1 start\n");
	int i;
	uthread_create(f2,arg);
	for(i=0; i<10000; i++);
	assert(uthread_join(2,(int*)arg)==UTHREAD_SUCCESS);
	assert(*((int*)arg) == UTHREAD_CANCELLED);
	assert(uthread_join(3,(int*)arg)==UTHREAD_SUCCESS);// should return success
	assert(uthread_join(1,(int*)arg)==UTHREAD_FAIL);
	msg("T1 end and T3 will return to run now\n");
	return 1;
}

int test7() {
	msg("\n*******************test 7: T0 start and will yield now****************\n");
	uthread_yield();
	//msg("eeeeeeeee\n");
	int i,arg;
	int *a=&arg;
	//char buf[3] = "ah\n";
	//write(1,buf,3);
	uthread_create(f1,a);
	/*char buf1[3] = "aa\n";
	write(1,buf1,3);
	for(i=0;i<5;i++){
		uthread_yield();
	}*/
	//msg("xxxxxxx\n");
	uthread_yield();
	//msg("yyyyyyy1\n");
	uthread_yield();
	//msg("yyyyyyy2\n");
	uthread_yield();
	//msg("yyyyyyy3\n");
	for(i=0;i<30;i++)
		uthread_yield();
	//char buf2[3] = "xx\n";
	//write(1,buf1,3);
	//write(1,buf,3);
	//msg("yyyyyyyy\n");
	//for (i=1; i<500000000; ++i);

	assert(uthread_cancel(1)==UTHREAD_SUCCESS);//success
	assert(uthread_cancel(2)==UTHREAD_INVALID);
	assert(uthread_cancel(3)==UTHREAD_SUCCESS);//success
	assert(uthread_join(1, a)==UTHREAD_INVALID);
	assert(uthread_join(2, a)==UTHREAD_INVALID);
	assert(uthread_join(3, a)==UTHREAD_SUCCESS);//success
	msg("T0 end. Exiting...\n");
	return 0;
}

/*
 * expected output:
 *
 * T0 start and will yield now
 * T1 start
 * T2 start and will yield now
 * T3 start and will yield now
 * T2 will join T3 now
 * T2 cancelled from T3 and now T3 will join T1
 * T1 end and T3 will return to run now
 * T3 end
 * T0 end. Exiting...
 *
 */

